#ifndef __DYNOBJECT__HH__
#define __DYNOBJECT__HH__

#include <dyntype/membuf.h>
#include <string>
using namespace std;

#include <dyntype/dyntype.h>

class DynObjectRef {
public:
  DynType* dyntype;
  membuf_t o_buf;
};

class DynObject {
public:
  DynObject();
  DynObject(int v) {
    DynType* dt = ::get_dyntype<int>();
    set_o_buf(dt, membuf_t((char*)&v, sizeof(v)));
  }
  DynObject(double v) {
    DynType* dt = ::get_dyntype<double>();
    set_o_buf(dt, membuf_t((char*)&v, sizeof(v)));
  }
  template <int N>
    DynObject(const CharN<N>& v) {
    DynType* dt = ::get_dyntype<CharN<N> >();
    set_o_buf(dt, membuf_t((char*)&v, sizeof(v)));
  }    

  DynObject(DynType* dyntype);
  DynObject(DynType* dyntype, const membuf_t& buf);

  DynObject(const DynObject&);
  DynObject& operator=(const DynObject&);
  virtual ~DynObject();

  void set_o_buf(DynType*);
  void set_o_buf(const membuf_t& o_buf); // dyntype set earlier
  void set_o_buf(DynType*, const membuf_t& o_buf);
  const membuf_t& get_o_buf() const { return o_buf; }

  bool equal(const DynObject&) const;
  bool less(const DynObject&) const;
  bool is_none() const;

  DynType* get_dyntype() const { return dyntype; }
  void print(ostream&) const;

  // converts value to membuf
  template <typename T> void set_value(const T& value);

  template <typename T> T get_value() const;

  template <typename T>
  void set_member(const T& value,
		  const string& m,		  
		  size_t cardinality_index = 0);
  
  template <typename T>
  const T& get_member(const string& m, size_t cardinality_index = 0) const;


private:
  DynType* dyntype;
  membuf_t o_buf;
};

inline bool operator==(const DynObject& l, const DynObject& r)
{
  return l.equal(r);
}

inline bool operator!=(const DynObject& l, const DynObject& r)
{
  return !l.equal(r);
}

inline bool operator<(const DynObject& l, const DynObject& r)
{
  return l.less(r);
}

inline ostream& operator<<(ostream& out, const DynObject& o)
{
  o.print(out);
  return out;
}

//  inlines

// converts value to membuf
template <typename T>
inline
void DynObject::set_value(const T& value) 
{
  set_o_buf(::get_dyntype<T>(), membuf_t((char*)&value, sizeof(T)));
}

template <typename T>
inline
T DynObject::get_value() const 
{
  return *(T*)o_buf.buf;
}    

template <typename T>
inline
void DynObject::set_member(const T& value,
			   const string& m,			   
			   size_t cardinality_index)
{
  CompoundDynType* c_dyntype = dynamic_cast<CompoundDynType*>(dyntype);
  if (c_dyntype == 0) {
    throw Error(__FILE__, __LINE__, "DynObject::set_member: dynobject has non-compund dyntype " + dyntype->get_name());
  }
  
  DynType* m_dyntype = c_dyntype->get_member_dyntype(m);
  if (m_dyntype == 0) {
    throw Error(__FILE__, __LINE__, "DynObject::set_member: dyntype ptr is null");
  }
  if (m_dyntype != ::get_dyntype<T>()) {
    throw Error(__FILE__, __LINE__, "DynObject::set_member: dyntype mismatch for template arg and member dyntype");
  }
  size_t m_offset = c_dyntype->get_member_offset(m, cardinality_index);
  char* buf = o_buf.buf;
  *(T*)(&buf[m_offset]) = value;
}

template <typename T>
inline
const T& 
DynObject::get_member(const string& m, size_t cardinality_index) const 
{
  CompoundDynType* c_dyntype = dynamic_cast<CompoundDynType*>(dyntype);
  if (c_dyntype == 0) {
    throw Error(__FILE__, __LINE__, "DynObject::get_member: dynobject has non-compund dyntype");
  }
  
  DynType* m_dyntype = c_dyntype->get_member_dyntype(m);
  if (m_dyntype == 0) {
    throw Error(__FILE__, __LINE__, "DynObject::get_member: dyntype ptr is null");
  }
  if (m_dyntype != ::get_dyntype<T>()) {
    throw Error(__FILE__, __LINE__, "DynObject::get_member: dyntype mismatch for template arg and member dyntype");
  }
  size_t m_offset = c_dyntype->get_member_offset(m, cardinality_index);
  const char* buf = o_buf.buf;
  return *(const T*)(&buf[m_offset]);
}

#endif
